#include "StdAfx.h"
#include "Skydome.h"
#include "Level.h"
#include "GraphCamera.h"
#include "PhysX.h"

#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

Skydome::Skydome(Level *pLevel, InitInfo& info)
	:LevelElement()
	,m_IntInfo(info)
	,m_pVertexLayout(0)
	,m_pVertexBuffer(0)
	,m_pDefaultEffect(0)	
	,m_pDefaultTechnique(0)	
	,m_pTextureLow(0)
	,m_pWorldViewProjectionVariable(NULL)
	,m_pWorldVariable(0)
	,m_DomeN(info.DomeN)
	,m_bReflection(false)
	,m_nThetaSides(256)// Azimuth
    ,m_nPhiSides(256)	// Zenith
	,m_fRadiusHoriz(info.radiusHoriz)
	,m_fRadiusUp(info.radiusUp)
{
	m_pLevel = pLevel;
	m_vCenter = D3DXVECTOR3(0, 0, 0);
}

Skydome::~Skydome(void)
{
	SafeRelease(m_pVertexBuffer );
	SafeRelease(m_pVertexLayout);
}
void Skydome::Initialize(ContentManager *pContentManager)
{
	CreateEffect(pContentManager);
	//ResetWorldMatrix();
	//Translate(D3DXVECTOR3(0.0f, 20.0f, 0.0f));
	//Loading the textures
	m_pTextureLow=		pContentManager->GetTexture(m_pLevel->GetDevice(), m_IntInfo.Texture);
	
	CreateSkydome();

	DefineInputlayout();
	BuildVertexBuffer();

	ResetWorldMatrix();
	//m_IntInfo.Position.y -= 25.0f;
	Translate(m_IntInfo.Position);
}
void Skydome::Draw(const RenderContext* pRenderContext)
{
	if(!m_pDefaultTechnique )
	{
		MessageBox(0,_T("No Technique"),_T("ERROR"),0);
		return;
	}
	if(!m_pVertexBuffer)
	{
		MessageBox(0,_T("No Vertices"),_T("ERROR"),0);
		return;
	}	
	

	// Update Shader matrix variables
	D3DXMATRIX matView;
	if(!m_bReflection)
		matView = pRenderContext->GetCamera()->GetView();
	else
		matView = m_ReflectionViewMatrix;

	D3DXMATRIX matProj = pRenderContext->GetCamera()->GetProj();
	m_pWorldViewProjectionVariable->SetMatrix((float*)&(m_World*matView*matProj));

	m_pWorldVariable->SetMatrix((float*)&(m_World));

	D3DXVECTOR3 pos = pRenderContext->GetCamera()->GetPos();

	m_pCubemapVariable->SetResource(m_pTextureLow);


	float fReflectance = 0.1f;

    D3DXVECTOR3 vecBetaR           = m_pScatterInfo.BetaRay * m_pScatterInfo.BetaRayMultiplier;
    D3DXVECTOR3 vecBetaDashR       = m_pScatterInfo.BetaDashRay * m_pScatterInfo.BetaRayMultiplier ;
    D3DXVECTOR3 vecBetaM           = m_pScatterInfo.BetaMie * m_pScatterInfo.BetaMieMultiplier ;
    D3DXVECTOR3 vecBetaDashM       = m_pScatterInfo.BetaDashMie * m_pScatterInfo.BetaMieMultiplier ;
    D3DXVECTOR3 vecBetaRM          = vecBetaR + vecBetaM;

    D3DXVECTOR3 vecOneOverBetaRM   = D3DXVECTOR3( 1.0f / vecBetaRM.x, 1.0f / vecBetaRM.y, 1.0f / vecBetaRM.z );
    D3DXVECTOR3 vecG               = D3DXVECTOR3( 1.0f - m_pScatterInfo.HGgFunction * m_pScatterInfo.HGgFunction, 1.0f + m_pScatterInfo.HGgFunction * m_pScatterInfo.HGgFunction, 2.0f * m_pScatterInfo.HGgFunction );
    D3DXVECTOR3 vecTermMulitpliers = D3DXVECTOR4(m_pScatterInfo.InscatteringMultiplier, 0.138f * fReflectance, 0.113f * fReflectance, 0.08f * fReflectance);


	m_pEyePosVariable->SetFloatVector((float*)pos);
	m_pSunDirectionVariable->SetFloatVector((float*)m_pScatterInfo.SunDirection);
	m_pBetaRPlusBetaMVariable->SetFloatVector((float*)vecBetaRM);
	m_pHCgVariable->SetFloatVector((float*)vecG);
	m_pBetaDahRVariable->SetFloatVector((float*)vecBetaDashR);
	m_pBetaDashMVarable->SetFloatVector((float*)vecBetaDashM);
	m_pOneOverBetaRVariable->SetFloatVector((float*)vecOneOverBetaRM);
	m_pMultiepliersVariable->SetFloatVector((float*)vecTermMulitpliers);
	m_SunColorAndIntensity->SetFloatVector((float*)m_pScatterInfo.SunColorAndIntensity);
	
	//FogINfo
	m_pFogModeVariable->SetInt(pRenderContext->GetFogInfo().FogMode);
	m_pFogStartVariable->SetFloat(pRenderContext->GetFogInfo().FogStart);
	m_pFogEndVariable->SetFloat(pRenderContext->GetFogInfo().FogEnd);
	m_pFogDensityVariable->SetFloat(pRenderContext->GetFogInfo().FogDensity);
	m_pFogMaxHeightVariable->SetFloat(pRenderContext->GetFogInfo().FogMaxHeight);
	D3DXCOLOR fogcol = pRenderContext->GetFogInfo().FogColor;
	m_pFogColorVariable->SetRawValue(&fogcol, 0,sizeof(D3DXCOLOR));

	// Set the input layout
    m_pLevel->GetDevice()->IASetInputLayout( m_pVertexLayout );

	// Set vertex buffer(s)
    UINT offset = 0;
	UINT vertexBufferStride = sizeof(VertexPosTex);
    m_pLevel->GetDevice()->IASetVertexBuffers( 0, 1, &m_pVertexBuffer, &vertexBufferStride, &offset );

    // Set primitive topology
    m_pLevel->GetDevice()->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );

    D3D10_TECHNIQUE_DESC techDesc;
    m_pDefaultTechnique->GetDesc( &techDesc );
    for( UINT p = 0; p < techDesc.Passes; ++p )
    {
		m_pDefaultTechnique->GetPassByIndex(p)->Apply(0);
		int startVertex = 0;
		int numFacesPerStrip = m_nPhiSides * 2;
		for (int i = 0; i < m_nThetaSides; i++)
		{
			m_pLevel->GetDevice()->Draw( numFacesPerStrip, startVertex); 
			startVertex += (m_nPhiSides + 1) * 2;
		} 
    }
}

void Skydome::CreateEffect(ContentManager *pContentManager)
{
	m_pDefaultEffect = pContentManager->GetEffect(m_pLevel->GetDevice(),  _T("./Effect/Skydome2.fx"));
	
	//get first technique found
	m_pDefaultTechnique = m_pDefaultEffect->GetTechniqueByIndex(0);
	if(!m_pDefaultTechnique->IsValid())MessageBox(0,_T("Technique not valid"),_T("ERROR"),0);

	GetEffectVariables(m_pDefaultEffect);
}

void Skydome::GetEffectVariables(ID3D10Effect* pEffect)
{
	//get effect variables
	m_pWorldViewProjectionVariable = pEffect->GetVariableByName("WorldViewProj")->AsMatrix();
	if(!m_pWorldViewProjectionVariable->IsValid())MessageBox(0,_T("Getting EffectSemantic WorldViewProjection Failed"),_T("ERROR"),0);

	m_pWorldVariable = pEffect->GetVariableByName("WorldView")->AsMatrix();
	if(!m_pWorldVariable->IsValid())MessageBox(0,_T("Getting EffectSemantic World Failed"),_T("ERROR"),0);

	m_pCubemapVariable = pEffect->GetVariableByName("g_TextureDiffuse")->AsShaderResource();
	if(!m_pCubemapVariable->IsValid())MessageBox(0,_T("Getting EffectSemantic WorldMatrix Failed"),_T("ERROR"),0);

	m_pEyePosVariable = pEffect->GetVariableByName("EyePos")->AsVector();
	m_pSunDirectionVariable = pEffect->GetVariableByName("SunDir")->AsVector();
	m_pBetaRPlusBetaMVariable = pEffect->GetVariableByName("BetaRPlusBetaM")->AsVector();
	m_pHCgVariable = pEffect->GetVariableByName("HGg")->AsVector();
	m_pBetaDahRVariable = pEffect->GetVariableByName("BetaDashR")->AsVector();
	m_pBetaDashMVarable = pEffect->GetVariableByName("BetaDashM")->AsVector();
	m_pOneOverBetaRVariable = pEffect->GetVariableByName("OneOverBetaRPlusBetaM")->AsVector();
	m_pMultiepliersVariable = pEffect->GetVariableByName("Multipliers")->AsVector();
	m_SunColorAndIntensity = pEffect->GetVariableByName("SunColorAndIntensity")->AsVector();

	m_pFogModeVariable = pEffect->GetVariableByName("g_fogMode")->AsScalar();
	m_pFogStartVariable = pEffect->GetVariableByName("g_fogStart")->AsScalar();
	m_pFogEndVariable = pEffect->GetVariableByName("g_fogEnd")->AsScalar();
	m_pFogDensityVariable = pEffect->GetVariableByName("g_fogDensity")->AsScalar();
	m_pFogMaxHeightVariable = pEffect->GetVariableByName("g_fogMaxHeight")->AsScalar();
	m_pFogColorVariable = pEffect->GetVariableByName("g_fogColor")->AsVector();
}

void Skydome::DefineInputlayout()
{
	// Define the input layout DO NOT USE D3D10_APPEND_ALIGNED_ELEMENT 
	D3D10_INPUT_ELEMENT_DESC layout[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
	};
	UINT numElements = sizeof(layout)/sizeof(layout[0]);

    // Create the input layout
    D3D10_PASS_DESC PassDesc;
	// Get the pass decriptor from the effect technique
    m_pDefaultTechnique->GetPassByIndex( 0 )->GetDesc( &PassDesc );
    HR(m_pLevel->GetDevice()->CreateInputLayout( layout, numElements, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &m_pVertexLayout ));

}

void Skydome::BuildVertexBuffer()
{
	//fill a buffer description to copy the vertexdata into graphics memory
	D3D10_BUFFER_DESC bd;
	bd.Usage = D3D10_USAGE_DEFAULT;
	bd.ByteWidth = sizeof( VertexPosTex ) * m_VecVertices.size();
	bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;

	D3D10_SUBRESOURCE_DATA initData;
	initData.pSysMem = m_VecVertices.data();
	//create a ID3D10Buffer in graphics memory containing the vertex info
	HR(m_pLevel->GetDevice()->CreateBuffer( &bd, &initData, &m_pVertexBuffer ));
}
void Skydome::DrawReflection(const RenderContext* pRenderContext, D3DXMATRIX reflectionviewmatrix, D3DXPLANE reflectionplane)
{
	m_bReflection = true;

	m_ReflectionViewMatrix = reflectionviewmatrix;
	//ID3D10EffectTechnique* PreviousTechnique = m_pDefaultTechnique;

	//m_pDefaultTechnique = m_pDefaultEffect->GetTechniqueByName("Reflection");
	//if(!m_pDefaultTechnique->IsValid())MessageBox(0,_T("Technique not valid"),_T("ERROR"),0);

	Draw(pRenderContext);

	//m_pDefaultTechnique = PreviousTechnique;
	m_bReflection = false;
}
void Skydome::CreateSkydome()
{    
   //m_pVB->Lock(0, 0, (BYTE**)&pVertex, 0);

    int nNumVertices = m_nThetaSides * 2 * (m_nPhiSides + 1);

    //m_pVB->Lock(0, 0, (BYTE**)&pVertex, 0);

    float degToRadian = (float)D3DX_PI  / 180.0f;

    int index = 0;
    float x1, x2, y, z1, z2;
    for (int i = 0; i < m_nThetaSides; i++)
    {
        float fTheta1 = (float)i / m_nThetaSides * (360*degToRadian);
        float fTheta2 = (float)(i + 1) / m_nThetaSides * (360 * degToRadian);
        for (int j = 0; j <= m_nPhiSides; j++)
        {
            float fPhi = (float)j / m_nPhiSides * (120 * degToRadian);

            x1 = (float)sinf(fPhi) * (float)cosf(fTheta1) * m_fRadiusHoriz;
            z1 = (float)sinf(fPhi) * (float)sinf(fTheta1) * m_fRadiusHoriz;
            x2 = (float)sinf(fPhi) * (float)cosf(fTheta2) * m_fRadiusHoriz;
            z2 = (float)sinf(fPhi) * (float)sinf(fTheta2) * m_fRadiusHoriz;
            y = (float)cosf(fPhi) * m_fRadiusUp;

			VertexPosTex v0;
			VertexPosTex v1;
            //pVertex[index] = new CustomVertex.PositionOnly();

            v0.pos = D3DXVECTOR3(x1, y, z1) + m_vCenter;
            v0.tex.x = (float)i / m_nThetaSides;
            v0.tex.y = (float)j / m_nPhiSides;

            index++;

            v1.pos = D3DXVECTOR3(x2, y, z2) + m_vCenter;
            v1.tex.x = (float)(i + 1) / m_nThetaSides;
            v1.tex.y = (float)j / m_nPhiSides;

            index++;

			m_VecVertices.push_back(v0);
			m_VecVertices.push_back(v1);
        }
    }
}